package com.haiqiu.syslog.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import com.haiqiu.common.result.ResultData;
import com.haiqiu.common.utils.web.JwtTokenUtil;
import com.haiqiu.common.utils.web.ServletUtil;
import com.haiqiu.syslog.annotation.NotLog;
import com.haiqiu.syslog.entity.SysLog;
import com.haiqiu.syslog.service.SysLogService;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @author HaiQiu
 * @ClassName LogApset.java
 * @Description TODO
 * @createTime 2021年08月13日
 */
@Aspect
@Component
@Slf4j
@Order(0)
public class LogAspect {

    @Autowired
    private SysLogService sysLogService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    /**
     * 是否存在注解，如果存在就不记录日志
     */
    private static NotLog getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(NotLog.class);
        }
        return null;
    }

    /**
     * 切入点，项目controller路径的所有方法
     *
     * @Pointcut("execution(任意返回值
     */
    @Pointcut("execution(public * com.haiqiu.*.controller.*.*(..))")
    public void controllerMethod() {

    }

    /**
     * 切入点，security异常切点
     *
     * @Pointcut("execution(任意返回值
     */
    @Pointcut("execution(public * com.haiqiu.*.handle.*.*(..))")
    public void securityHandle() {

    }

    /**
     * 切入点，security异常切点
     *
     * @Pointcut("execution(任意返回值
     */
    @Pointcut("execution(public * com.haiqiu.*.config.*.*(..))")
    public void securityFilter() {

    }

    SysLog sysLog = new SysLog();

    @Before("controllerMethod()")
    public void LogRequestInfo(JoinPoint joinPoint) {
        //获得注解不记录日志
        NotLog controllerLog = getAnnotationLog(joinPoint);
        //不存在注解则记录日志
        if (controllerLog == null) {
            HttpServletRequest request = ServletUtil.getHttpServletRequest();

            sysLog.setBrowser(ServletUtil.getBrowser(request));
            sysLog.setRequestIp(ServletUtil.getIpAddr(request));
            sysLog.setMethod(request.getMethod());
            sysLog.setRestUrl(request.getRequestURL().toString());
            sysLog.setUsername(jwtTokenUtil.getUsername(request));
            sysLog.setRequestAddress(ServletUtil.getAddress(sysLog.getRequestIp()));
            sysLog.setOs(ServletUtil.getOs(request));

            //处理请求参数
            StringBuilder requestLog = new StringBuilder();
            Signature signature = joinPoint.getSignature();
            sysLog.setOperation(((MethodSignature) signature).getMethod().getAnnotation(Operation.class).description());
            requestLog.append(((MethodSignature) signature).getMethod().getAnnotation(Operation.class).description()).append("\t")
                    .append("请求信息：").append("URL = {").append(request.getRequestURI()).append("},\t")
                    .append("请求方式 = {").append(request.getMethod()).append("},\t")
                    .append("请求IP = {").append(request.getRemoteAddr()).append("},\t")
                    .append("类方法 = {").append(signature.getDeclaringTypeName()).append(".")
                    .append(signature.getName()).append("},\t");

            // 处理请求参数
            String[] paramNames = ((MethodSignature) signature).getParameterNames();
            Object[] paramValues = joinPoint.getArgs();
            if (paramValues.length == 0) {
                requestLog.append("请求参数 = {} ");
            } else {
                requestLog.append("请求参数 = [");
                int i = -1;
                for (Object paramValue : paramValues) {
                    i++;
                    if (paramValue == null) {
                        requestLog.append(paramNames[i]).append("=").append(",]");
                    } else {
                        if (paramValue instanceof MultipartFile
                                || paramValue instanceof ServletRequest
                                || paramValue instanceof ServletResponse
                                || paramValue.toString().contains("MultipartFile")) {
                            requestLog.append(paramNames[i]).append("=").append(paramValues.toString()).append(",]");
                        } else {
                            if (getJSONType(paramValues[i].toString())) {
                                requestLog.append(paramNames[i]).append("=").append(JSONObject.toJSONString(paramValues[i])).append(",]");
                            } else {
                                requestLog.append(paramNames[i]).append("=").append(paramValues[i].toString()).append(",]");
                            }
                        }
                    }
                }
            }

            log.info(requestLog.toString());
            sysLog.setRequestParam(requestLog.toString());
        }
    }


    /**
     * 方法执行后
     *
     * @param resultData 返回实体
     * @throws Exception
     */
    @AfterReturning(returning = "resultData", pointcut = "controllerMethod()")
    public void logResultDataInfo(JoinPoint joinPoint,ResultData resultData) throws Exception{
        //获得注解不记录日志
        NotLog controllerLog = getAnnotationLog(joinPoint);
        //不存在注解则记录日志
        if (controllerLog == null) {
        String result = ("请求结果：" + resultData.getCode() + "，" + resultData.getMessage());
        log.info(result);
        sysLog.setResult(result);
        if (sysLog.getRestUrl().contains("list") && sysLog.getMethod().equals("POST")) {
            sysLog.setResponseJson("数据量较大，暂不记录数据");
        } else {
            //格式化返回的json数据
            String toJSONString = JSONObject.toJSONString(resultData);
            sysLog.setResponseJson(toJSONString);
        }

            sysLogService.insert(sysLog);
        }

    }

    /**
     * 配置异常通知
     *
     * @param joinPoint join point for advice
     * @param e         exception
     */
    @AfterThrowing(pointcut = "controllerMethod()||securityHandle()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
        //获得注解不记录日志
        NotLog controllerLog = getAnnotationLog(joinPoint);
        //不存在注解则记录日志
        if (controllerLog == null) {
        HttpServletRequest request = ServletUtil.getHttpServletRequest();

        sysLog.setBrowser(ServletUtil.getBrowser(request));
        sysLog.setRequestIp(ServletUtil.getIpAddr(request));
        sysLog.setMethod(request.getMethod());
        sysLog.setRestUrl(request.getRequestURL().toString());
        sysLog.setUsername(jwtTokenUtil.getUsername(request));
        sysLog.setRequestAddress(ServletUtil.getAddress(sysLog.getRequestIp()));
        sysLog.setOs(ServletUtil.getOs(request));

        //处理请求参数
        StringBuilder requestLog = new StringBuilder();
        Signature signature = joinPoint.getSignature();
        Operation annotation = ((MethodSignature) signature).getMethod().getAnnotation(Operation.class);
        String api = "请求拦截";
        if (annotation != null) {
            api = annotation.description();
        }
        requestLog.append(api).append("，")
                .append("请求信息：").append("URL = {").append(request.getRequestURI()).append("},\t")
                .append("请求方式 = {").append(request.getMethod()).append("},\t")
                .append("请求IP = {").append(request.getRemoteAddr()).append("},\t")
                .append("类方法 = {").append(signature.getDeclaringTypeName()).append(".")
                .append(signature.getName()).append("},\t");

        // 处理请求参数
        String[] paramNames = ((MethodSignature) signature).getParameterNames();
        Object[] paramValues = joinPoint.getArgs();
        if (paramValues.length == 0) {
            requestLog.append("请求参数 = {} ");
        } else {
            requestLog.append("请求参数 = [");
            int i = -1;
            for (Object paramValue : paramValues) {
                i++;
                if (paramValue == null) {
                    requestLog.append(paramNames[i]).append("=").append("null,");
                } else if (paramValue instanceof MultipartFile
                        || paramValue instanceof ServletRequest
                        || paramValue instanceof ServletResponse
                        || paramValue.toString().contains("MultipartFile")) {
                    requestLog.append(paramNames[i]).append("=").append(paramValues.toString()).append(",");
                } else {
                    if (getJSONType(paramValues[i].toString())) {
                        requestLog.append(paramNames[i]).append("=").append(JSONObject.toJSONString(paramValues[i])).append(",");
                    } else {
                        requestLog.append(paramNames[i]).append("=").append(paramValues[i].toString()).append(",");
                    }
                }
            }
            requestLog.append("]");
        }
        log.info(requestLog.toString());
        sysLog.setRequestParam(requestLog.toString());
        //格式化返回的json数据
        Object o = JSON.toJSON(e);
        JSONObject jsonObject = JSONObject.parseObject(o.toString());
        sysLog.setResponseJson(jsonObject.toJSONString());
        String code = jsonObject.getString("code") == null ? "" : jsonObject.getString("code");
        String msg = jsonObject.getString("msg") == null ? "" : jsonObject.getString("msg");
        String localizedMessage = jsonObject.getString("localizedMessage") == null ? "" : jsonObject.getString("localizedMessage");

        sysLog.setResult("请求结果：" + code
                + " " + msg
                + " " + localizedMessage);

            sysLogService.insert(sysLog);
        }
    }


    /**
     * 判断是否为json格式数据
     *
     * @param str
     * @return
     */
    public boolean getJSONType(String str) {
        boolean result = false;
        if (StringUtils.isNotBlank(str)) {
            str = str.trim();
            if (str.startsWith("{") && str.endsWith("}")) {
                result = true;
            } else if (str.startsWith("[") && str.endsWith("]")) {
                result = true;
            }
        }
        return result;
    }
}
